home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / xulrunner-1.9.1.5 / components / nsHandlerService.js < prev    next >
Text File  |  2009-11-08  |  54KB  |  1,432 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is the Mozilla browser.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla.
  17.  * Portions created by the Initial Developer are Copyright (C) 2007
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Myk Melez <myk@mozilla.org>
  22.  *   Dan Mosedale <dmose@mozilla.org>
  23.  *   Florian Queze <florian@queze.net>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. const Ci = Components.interfaces;
  40. const Cc = Components.classes;
  41. const Cu = Components.utils;
  42. const Cr = Components.results;
  43.  
  44.  
  45. const CLASS_MIMEINFO        = "mimetype";
  46. const CLASS_PROTOCOLINFO    = "scheme";
  47.  
  48.  
  49. // namespace prefix
  50. const NC_NS                 = "http://home.netscape.com/NC-rdf#";
  51.  
  52. // the most recent default handlers that have been injected.  Note that
  53. // this is used to construct an RDF resource, which needs to have NC_NS
  54. // prepended, since that hasn't been done yet
  55. const DEFAULT_HANDLERS_VERSION = "defaultHandlersVersion";
  56.  
  57. // type list properties
  58.  
  59. const NC_MIME_TYPES         = NC_NS + "MIME-types";
  60. const NC_PROTOCOL_SCHEMES   = NC_NS + "Protocol-Schemes";
  61.  
  62. // content type ("type") properties
  63.  
  64. // nsIHandlerInfo::type
  65. const NC_VALUE              = NC_NS + "value";
  66. const NC_DESCRIPTION        = NC_NS + "description";
  67.  
  68. // additional extensions
  69. const NC_FILE_EXTENSIONS    = NC_NS + "fileExtensions";
  70.  
  71. // references nsIHandlerInfo record
  72. const NC_HANDLER_INFO       = NC_NS + "handlerProp";
  73.  
  74. // handler info ("info") properties
  75.  
  76. // nsIHandlerInfo::preferredAction
  77. const NC_SAVE_TO_DISK       = NC_NS + "saveToDisk";
  78. const NC_HANDLE_INTERNALLY  = NC_NS + "handleInternal";
  79. const NC_USE_SYSTEM_DEFAULT = NC_NS + "useSystemDefault";
  80.  
  81. // nsIHandlerInfo::alwaysAskBeforeHandling
  82. const NC_ALWAYS_ASK         = NC_NS + "alwaysAsk";
  83.  
  84. // references nsIHandlerApp records
  85. const NC_PREFERRED_APP      = NC_NS + "externalApplication";
  86. const NC_POSSIBLE_APP       = NC_NS + "possibleApplication";
  87.  
  88. // handler app ("handler") properties
  89.  
  90. // nsIHandlerApp::name
  91. const NC_PRETTY_NAME        = NC_NS + "prettyName";
  92.  
  93. // nsILocalHandlerApp::executable
  94. const NC_PATH               = NC_NS + "path";
  95.  
  96. // nsIWebHandlerApp::uriTemplate
  97. const NC_URI_TEMPLATE       = NC_NS + "uriTemplate";
  98.  
  99. // nsIDBusHandlerApp::service
  100. const NC_SERVICE            = NC_NS + "service";
  101.  
  102. // nsIDBusHandlerApp::method
  103. const NC_METHOD             = NC_NS + "method";
  104.  
  105. // nsIDBusHandlerApp::objectPath
  106. const NC_OBJPATH            = NC_NS + "objectPath";
  107.  
  108. // nsIDBusHandlerApp::dbusInterface
  109. const NC_INTERFACE            = NC_NS + "dBusInterface";
  110.  
  111. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  112.  
  113.  
  114. function HandlerService() {
  115.   this._init();
  116. }
  117.  
  118. HandlerService.prototype = {
  119.   //**************************************************************************//
  120.   // XPCOM Plumbing
  121.  
  122.   classDescription: "Handler Service",
  123.   classID:          Components.ID("{32314cc8-22f7-4f7f-a645-1a45453ba6a6}"),
  124.   contractID:       "@mozilla.org/uriloader/handler-service;1",
  125.   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIHandlerService]),
  126.  
  127.  
  128.   //**************************************************************************//
  129.   // Initialization & Destruction
  130.   
  131.   _init: function HS__init() {
  132.     // Observe profile-before-change so we can switch to the datasource
  133.     // in the new profile when the user changes profiles.
  134.     this._observerSvc.addObserver(this, "profile-before-change", false);
  135.  
  136.     // Observe xpcom-shutdown so we can remove these observers
  137.     // when the application shuts down.
  138.     this._observerSvc.addObserver(this, "xpcom-shutdown", false);
  139.  
  140.     // Observe profile-do-change so that non-default profiles get upgraded too
  141.     this._observerSvc.addObserver(this, "profile-do-change", false);
  142.     
  143.     // do any necessary updating of the datastore
  144.     this._updateDB();
  145.   },
  146.  
  147.   _updateDB: function HS__updateDB() {
  148.     try {
  149.       var defaultHandlersVersion = this._datastoreDefaultHandlersVersion;
  150.     } catch(ex) {
  151.       // accessing the datastore failed, we can't update anything
  152.       return;
  153.     }
  154.  
  155.     try {
  156.       // if we don't have the current version of the default prefs for
  157.       // this locale, inject any new default handers into the datastore
  158.       if (defaultHandlersVersion < this._prefsDefaultHandlersVersion) {
  159.  
  160.         // set the new version first so that if we recurse we don't
  161.         // call _injectNewDefaults several times
  162.         this._datastoreDefaultHandlersVersion =
  163.           this._prefsDefaultHandlersVersion;
  164.         this._injectNewDefaults();
  165.       } 
  166.     } catch (ex) {
  167.       // if injecting the defaults failed, set the version back to the
  168.       // previous value
  169.       this._datastoreDefaultHandlersVersion = defaultHandlersVersion;
  170.     }
  171.   },
  172.  
  173.   get _currentLocale() {
  174.     var chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].
  175.                          getService(Ci.nsIXULChromeRegistry);
  176.     var currentLocale = chromeRegistry.getSelectedLocale("global");
  177.     return currentLocale;
  178.   }, 
  179.  
  180.   _destroy: function HS__destroy() {
  181.     this._observerSvc.removeObserver(this, "profile-before-change");
  182.     this._observerSvc.removeObserver(this, "xpcom-shutdown");
  183.     this._observerSvc.removeObserver(this, "profile-do-change");
  184.  
  185.     // XXX Should we also null references to all the services that get stored
  186.     // by our memoizing getters in the Convenience Getters section?
  187.   },
  188.  
  189.   _onProfileChange: function HS__onProfileChange() {
  190.     // Lose our reference to the datasource so we reacquire it
  191.     // from the new profile the next time we need it.
  192.     this.__ds = null;
  193.   },
  194.  
  195.   _isInHandlerArray: function HS__isInHandlerArray(aArray, aHandler) {
  196.     var enumerator = aArray.enumerate();
  197.     while (enumerator.hasMoreElements()) {
  198.       let handler = enumerator.getNext();
  199.       handler.QueryInterface(Ci.nsIHandlerApp);
  200.       if (handler.equals(aHandler))
  201.         return true;
  202.     }
  203.     
  204.     return false;
  205.   },
  206.  
  207.   // note that this applies to the current locale only 
  208.   get _datastoreDefaultHandlersVersion() {
  209.     var version = this._getValue("urn:root", NC_NS + this._currentLocale +
  210.                                              "_" + DEFAULT_HANDLERS_VERSION);
  211.     
  212.     return version ? version : -1;
  213.   },
  214.  
  215.   set _datastoreDefaultHandlersVersion(aNewVersion) {
  216.     return this._setLiteral("urn:root", NC_NS + this._currentLocale + "_" + 
  217.                             DEFAULT_HANDLERS_VERSION, aNewVersion);
  218.   },
  219.  
  220.   get _prefsDefaultHandlersVersion() {
  221.     // get handler service pref branch
  222.     var prefSvc = Cc["@mozilla.org/preferences-service;1"].
  223.                   getService(Ci.nsIPrefService);
  224.     var handlerSvcBranch = prefSvc.getBranch("gecko.handlerService.");
  225.  
  226.     // get the version of the preferences for this locale
  227.     return Number(handlerSvcBranch.
  228.                   getComplexValue("defaultHandlersVersion", 
  229.                                   Ci.nsIPrefLocalizedString).data);
  230.   },
  231.   
  232.   _injectNewDefaults: function HS__injectNewDefaults() {
  233.     // get handler service pref branch
  234.     var prefSvc = Cc["@mozilla.org/preferences-service;1"].
  235.                   getService(Ci.nsIPrefService);
  236.  
  237.     let schemesPrefBranch = prefSvc.getBranch("gecko.handlerService.schemes.");
  238.     let schemePrefList = schemesPrefBranch.getChildList("", {}); 
  239.  
  240.     var schemes = {};
  241.  
  242.     // read all the scheme prefs into a hash
  243.     for each (var schemePrefName in schemePrefList) {
  244.  
  245.       let [scheme, handlerNumber, attribute] = schemePrefName.split(".");
  246.  
  247.       try {
  248.         var attrData =
  249.           schemesPrefBranch.getComplexValue(schemePrefName,
  250.                                             Ci.nsIPrefLocalizedString).data;
  251.         if (!(scheme in schemes))
  252.           schemes[scheme] = {};
  253.   
  254.         if (!(handlerNumber in schemes[scheme]))
  255.           schemes[scheme][handlerNumber] = {};
  256.         
  257.         schemes[scheme][handlerNumber][attribute] = attrData;
  258.       } catch (ex) {}
  259.     }
  260.  
  261.     let protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
  262.                    getService(Ci.nsIExternalProtocolService);
  263.     for (var scheme in schemes) {
  264.  
  265.       // This clause is essentially a reimplementation of 
  266.       // nsIExternalProtocolHandlerService.getProtocolHandlerInfo().
  267.       // Necessary because calling that from here would make XPConnect barf
  268.       // when getService tried to re-enter the constructor for this
  269.       // service.
  270.       let osDefaultHandlerFound = {};
  271.       let protoInfo = protoSvc.getProtocolHandlerInfoFromOS(scheme, 
  272.                                osDefaultHandlerFound);
  273.       
  274.       if (this.exists(protoInfo))
  275.         this.fillHandlerInfo(protoInfo, null);
  276.       else
  277.         protoSvc.setProtocolHandlerDefaults(protoInfo, 
  278.                                             osDefaultHandlerFound.value);
  279.  
  280.       // cache the possible handlers to avoid extra xpconnect traversals.      
  281.       let possibleHandlers = protoInfo.possibleApplicationHandlers;
  282.  
  283.       for each (var handlerPrefs in schemes[scheme]) {
  284.  
  285.         let handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
  286.                          createInstance(Ci.nsIWebHandlerApp);
  287.  
  288.         handlerApp.uriTemplate = handlerPrefs.uriTemplate;
  289.         handlerApp.name = handlerPrefs.name;                
  290.  
  291.         if (!this._isInHandlerArray(possibleHandlers, handlerApp)) {
  292.           possibleHandlers.appendElement(handlerApp, false);
  293.         }
  294.       }
  295.  
  296.       this.store(protoInfo);
  297.     }
  298.   },
  299.  
  300.   //**************************************************************************//
  301.   // nsIObserver
  302.   
  303.   observe: function HS__observe(subject, topic, data) {
  304.     switch(topic) {
  305.       case "profile-before-change":
  306.         this._onProfileChange();
  307.         break;
  308.       case "xpcom-shutdown":
  309.         this._destroy();
  310.         break;
  311.       case "profile-do-change":
  312.         this._updateDB();
  313.         break;  
  314.     }
  315.   },
  316.  
  317.  
  318.   //**************************************************************************//
  319.   // nsIHandlerService
  320.  
  321.   enumerate: function HS_enumerate() {
  322.     var handlers = Cc["@mozilla.org/array;1"].
  323.                    createInstance(Ci.nsIMutableArray);
  324.     this._appendHandlers(handlers, CLASS_MIMEINFO);
  325.     this._appendHandlers(handlers, CLASS_PROTOCOLINFO);
  326.     return handlers.enumerate();
  327.   },
  328.  
  329.   fillHandlerInfo: function HS_fillHandlerInfo(aHandlerInfo, aOverrideType) {
  330.     var type = aOverrideType || aHandlerInfo.type;
  331.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), type);
  332.  
  333.     // Determine whether or not information about this handler is available
  334.     // in the datastore by looking for its "value" property, which stores its
  335.     // type and should always be present.
  336.     if (!this._hasValue(typeID, NC_VALUE))
  337.       throw Cr.NS_ERROR_NOT_AVAILABLE;
  338.  
  339.     // Retrieve the human-readable description of the type.
  340.     if (this._hasValue(typeID, NC_DESCRIPTION))
  341.       aHandlerInfo.description = this._getValue(typeID, NC_DESCRIPTION);
  342.  
  343.     // Note: for historical reasons, we don't actually check that the type
  344.     // record has a "handlerProp" property referencing the info record.  It's
  345.     // unclear whether or not we should start doing this check; perhaps some
  346.     // legacy datasources don't have such references.
  347.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), type);
  348.  
  349.     aHandlerInfo.preferredAction = this._retrievePreferredAction(infoID);
  350.  
  351.     var preferredHandlerID =
  352.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), type);
  353.  
  354.     // Retrieve the preferred handler.
  355.     // Note: for historical reasons, we don't actually check that the info
  356.     // record has an "externalApplication" property referencing the preferred
  357.     // handler record.  It's unclear whether or not we should start doing
  358.     // this check; perhaps some legacy datasources don't have such references.
  359.     aHandlerInfo.preferredApplicationHandler =
  360.       this._retrieveHandlerApp(preferredHandlerID);
  361.  
  362.     // Fill the array of possible handlers with the ones in the datastore.
  363.     this._fillPossibleHandlers(infoID,
  364.                                aHandlerInfo.possibleApplicationHandlers,
  365.                                aHandlerInfo.preferredApplicationHandler);
  366.  
  367.     // If we have an "always ask" flag stored in the RDF, always use its
  368.     // value. Otherwise, use the default value stored in the pref service.
  369.     var alwaysAsk;
  370.     if (this._hasValue(infoID, NC_ALWAYS_ASK)) {
  371.       alwaysAsk = (this._getValue(infoID, NC_ALWAYS_ASK) != "false");
  372.     } else {
  373.       var prefSvc = Cc["@mozilla.org/preferences-service;1"].
  374.                     getService(Ci.nsIPrefService);
  375.       var prefBranch = prefSvc.getBranch("network.protocol-handler.");
  376.       try {
  377.         alwaysAsk = prefBranch.getBoolPref("warn-external." + type);
  378.       } catch (e) {
  379.         // will throw if pref didn't exist.
  380.         try {
  381.           alwaysAsk = prefBranch.getBoolPref("warn-external-default");
  382.         } catch (e) {
  383.           // Nothing to tell us what to do, so be paranoid and prompt.
  384.           alwaysAsk = true;
  385.         }
  386.       }
  387.     }
  388.     aHandlerInfo.alwaysAskBeforeHandling = alwaysAsk;
  389.  
  390.     // If the object represents a MIME type handler, then also retrieve
  391.     // any file extensions.
  392.     if (aHandlerInfo instanceof Ci.nsIMIMEInfo)
  393.       for each (let fileExtension in this._retrieveFileExtensions(typeID))
  394.         aHandlerInfo.appendExtension(fileExtension);
  395.   },
  396.  
  397.   store: function HS_store(aHandlerInfo) {
  398.     // FIXME: when we switch from RDF to something with transactions (like
  399.     // SQLite), enclose the following changes in a transaction so they all
  400.     // get rolled back if any of them fail and we don't leave the datastore
  401.     // in an inconsistent state.
  402.  
  403.     this._ensureRecordsForType(aHandlerInfo);
  404.     this._storePreferredAction(aHandlerInfo);
  405.     this._storePreferredHandler(aHandlerInfo);
  406.     this._storePossibleHandlers(aHandlerInfo);
  407.     this._storeAlwaysAsk(aHandlerInfo);
  408.  
  409.     // Write the changes to the database immediately so we don't lose them
  410.     // if the application crashes.
  411.     if (this._ds instanceof Ci.nsIRDFRemoteDataSource)
  412.       this._ds.Flush();
  413.   },
  414.  
  415.   exists: function HS_exists(aHandlerInfo) {
  416.     var found;
  417.  
  418.     try {
  419.       var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  420.       found = this._hasLiteralAssertion(typeID, NC_VALUE, aHandlerInfo.type);
  421.     } catch (e) {
  422.       // If the RDF threw (eg, corrupt file), treat as non-existent.
  423.       found = false;
  424.     }
  425.  
  426.     return found;
  427.   },
  428.  
  429.   remove: function HS_remove(aHandlerInfo) {
  430.     var preferredHandlerID =
  431.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  432.     this._removeAssertions(preferredHandlerID);
  433.  
  434.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  435.  
  436.     // Get a list of possible handlers.  After we have removed the info record,
  437.     // we'll check if any other info records reference these handlers, and we'll
  438.     // remove the handler records that aren't referenced by other info records.
  439.     var possibleHandlerIDs = [];
  440.     var possibleHandlerTargets = this._getTargets(infoID, NC_POSSIBLE_APP);
  441.     while (possibleHandlerTargets.hasMoreElements()) {
  442.       let possibleHandlerTarget = possibleHandlerTargets.getNext();
  443.       // Note: possibleHandlerTarget should always be an nsIRDFResource.
  444.       // The conditional is just here in case of a corrupt RDF datasource.
  445.       if (possibleHandlerTarget instanceof Ci.nsIRDFResource)
  446.         possibleHandlerIDs.push(possibleHandlerTarget.ValueUTF8);
  447.     }
  448.  
  449.     // Remove the info record.
  450.     this._removeAssertions(infoID);
  451.  
  452.     // Now that we've removed the info record, remove any possible handlers
  453.     // that aren't referenced by other info records.
  454.     for each (let possibleHandlerID in possibleHandlerIDs)
  455.       if (!this._existsResourceTarget(NC_POSSIBLE_APP, possibleHandlerID))
  456.         this._removeAssertions(possibleHandlerID);
  457.  
  458.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  459.     this._removeAssertions(typeID);
  460.  
  461.     // Now that there's no longer a handler for this type, remove the type
  462.     // from the list of types for which there are known handlers.
  463.     var typeList = this._ensureAndGetTypeList(this._getClass(aHandlerInfo));
  464.     var type = this._rdf.GetResource(typeID);
  465.     var typeIndex = typeList.IndexOf(type);
  466.     if (typeIndex != -1)
  467.       typeList.RemoveElementAt(typeIndex, true);
  468.  
  469.     // Write the changes to the database immediately so we don't lose them
  470.     // if the application crashes.
  471.     // XXX If we're removing a bunch of handlers at once, will flushing
  472.     // after every removal cause a significant performance hit?
  473.     if (this._ds instanceof Ci.nsIRDFRemoteDataSource)
  474.       this._ds.Flush();
  475.   },
  476.  
  477.   getTypeFromExtension: function HS_getTypeFromExtension(aFileExtension) {
  478.     var fileExtension = aFileExtension.toLowerCase();
  479.     var typeID;
  480.  
  481.     if (this._existsLiteralTarget(NC_FILE_EXTENSIONS, fileExtension))
  482.       typeID = this._getSourceForLiteral(NC_FILE_EXTENSIONS, fileExtension);
  483.  
  484.     if (typeID && this._hasValue(typeID, NC_VALUE)) {
  485.       let type = this._getValue(typeID, NC_VALUE);
  486.       if (type == "")
  487.         throw Cr.NS_ERROR_FAILURE;
  488.       return type;
  489.     }
  490.  
  491.     return "";
  492.   },
  493.  
  494.  
  495.   //**************************************************************************//
  496.   // Retrieval Methods
  497.  
  498.   /**
  499.    * Retrieve the preferred action for the info record with the given ID.
  500.    *
  501.    * @param aInfoID  {string}  the info record ID
  502.    *
  503.    * @returns  {integer}  the preferred action enumeration value
  504.    */
  505.   _retrievePreferredAction: function HS__retrievePreferredAction(aInfoID) {
  506.     if (this._getValue(aInfoID, NC_SAVE_TO_DISK) == "true")
  507.       return Ci.nsIHandlerInfo.saveToDisk;
  508.     
  509.     if (this._getValue(aInfoID, NC_USE_SYSTEM_DEFAULT) == "true")
  510.       return Ci.nsIHandlerInfo.useSystemDefault;
  511.     
  512.     if (this._getValue(aInfoID, NC_HANDLE_INTERNALLY) == "true")
  513.       return Ci.nsIHandlerInfo.handleInternal;
  514.  
  515.     return Ci.nsIHandlerInfo.useHelperApp;
  516.   },
  517.  
  518.   /**
  519.    * Fill an array of possible handlers with the handlers for the given info ID.
  520.    *
  521.    * @param aInfoID            {string}           the ID of the info record
  522.    * @param aPossibleHandlers  {nsIMutableArray}  the array of possible handlers
  523.    * @param aPreferredHandler  {nsIHandlerApp}    the preferred handler, if any
  524.    */
  525.   _fillPossibleHandlers: function HS__fillPossibleHandlers(aInfoID,
  526.                                                            aPossibleHandlers,
  527.                                                            aPreferredHandler) {
  528.     // The set of possible handlers should include the preferred handler,
  529.     // but legacy datastores (from before we added possible handlers) won't
  530.     // include the preferred handler, so check if it's included as we build
  531.     // the list of handlers, and, if it's not included, add it to the list.
  532.     if (aPreferredHandler)
  533.       aPossibleHandlers.appendElement(aPreferredHandler, false);
  534.  
  535.     var possibleHandlerTargets = this._getTargets(aInfoID, NC_POSSIBLE_APP);
  536.  
  537.     while (possibleHandlerTargets.hasMoreElements()) {
  538.       let possibleHandlerTarget = possibleHandlerTargets.getNext();
  539.       if (!(possibleHandlerTarget instanceof Ci.nsIRDFResource))
  540.         continue;
  541.  
  542.       let possibleHandlerID = possibleHandlerTarget.ValueUTF8;
  543.       let possibleHandler = this._retrieveHandlerApp(possibleHandlerID);
  544.       if (possibleHandler && (!aPreferredHandler ||
  545.                               !possibleHandler.equals(aPreferredHandler)))
  546.         aPossibleHandlers.appendElement(possibleHandler, false);
  547.     }
  548.   },
  549.  
  550.   /**
  551.    * Retrieve the handler app object with the given ID.
  552.    *
  553.    * @param aHandlerAppID  {string}  the ID of the handler app to retrieve
  554.    *
  555.    * @returns  {nsIHandlerApp}  the handler app, if any; otherwise null
  556.    */
  557.   _retrieveHandlerApp: function HS__retrieveHandlerApp(aHandlerAppID) {
  558.     var handlerApp;
  559.  
  560.     // If it has a path, it's a local handler; otherwise, it's a web handler.
  561.     if (this._hasValue(aHandlerAppID, NC_PATH)) {
  562.       let executable =
  563.         this._getFileWithPath(this._getValue(aHandlerAppID, NC_PATH));
  564.       if (!executable)
  565.         return null;
  566.  
  567.       handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
  568.                    createInstance(Ci.nsILocalHandlerApp);
  569.       handlerApp.executable = executable;
  570.     }
  571.     else if (this._hasValue(aHandlerAppID, NC_URI_TEMPLATE)) {
  572.       let uriTemplate = this._getValue(aHandlerAppID, NC_URI_TEMPLATE);
  573.       if (!uriTemplate)
  574.         return null;
  575.  
  576.       handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"].
  577.                    createInstance(Ci.nsIWebHandlerApp);
  578.       handlerApp.uriTemplate = uriTemplate;
  579.     }
  580.     else if (this._hasValue(aHandlerAppID, NC_SERVICE)) {
  581.       let service = this._getValue(aHandlerAppID, NC_SERVICE);
  582.       if (!service)
  583.         return null;
  584.       
  585.       let method = this._getValue(aHandlerAppID, NC_METHOD);
  586.       if (!method)
  587.         return null;
  588.       
  589.       let objpath = this._getValue(aHandlerAppID, NC_OBJPATH);
  590.       if (!objpath)
  591.         return null;
  592.       
  593.       let interface = this._getValue(aHandlerAppID, NC_INTERFACE);
  594.       if (!interface)
  595.         return null;
  596.       
  597.       handlerApp = Cc["@mozilla.org/uriloader/dbus-handler-app;1"].
  598.                    createInstance(Ci.nsIDBusHandlerApp);
  599.       handlerApp.service   = service;
  600.       handlerApp.method    = method;
  601.       handlerApp.objectPath   = objpath;
  602.       handlerApp.dBusInterface = interface;
  603.       
  604.     }
  605.     else
  606.       return null;
  607.  
  608.     handlerApp.name = this._getValue(aHandlerAppID, NC_PRETTY_NAME);
  609.  
  610.     return handlerApp;
  611.   },
  612.  
  613.   /*
  614.    * Retrieve file extensions, if any, for the MIME type with the given type ID.
  615.    *
  616.    * @param aTypeID  {string}  the type record ID
  617.    */
  618.   _retrieveFileExtensions: function HS__retrieveFileExtensions(aTypeID) {
  619.     var fileExtensions = [];
  620.  
  621.     var fileExtensionTargets = this._getTargets(aTypeID, NC_FILE_EXTENSIONS);
  622.  
  623.     while (fileExtensionTargets.hasMoreElements()) {
  624.       let fileExtensionTarget = fileExtensionTargets.getNext();
  625.       if (fileExtensionTarget instanceof Ci.nsIRDFLiteral &&
  626.           fileExtensionTarget.Value != "")
  627.         fileExtensions.push(fileExtensionTarget.Value);
  628.     }
  629.  
  630.     return fileExtensions;
  631.   },
  632.  
  633.   /**
  634.    * Get the file with the given path.  This is not as simple as merely
  635.    * initializing a local file object with the path, because the path might be
  636.    * relative to the current process directory, in which case we have to
  637.    * construct a path starting from that directory.
  638.    *
  639.    * @param aPath  {string}  a path to a file
  640.    *
  641.    * @returns {nsILocalFile} the file, or null if the file does not exist
  642.    */
  643.   _getFileWithPath: function HS__getFileWithPath(aPath) {
  644.     var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  645.  
  646.     try {
  647.       file.initWithPath(aPath);
  648.  
  649.       if (file.exists())
  650.         return file;
  651.     }
  652.     catch(ex) {
  653.       // Note: for historical reasons, we don't actually check to see
  654.       // if the exception is NS_ERROR_FILE_UNRECOGNIZED_PATH, which is what
  655.       // nsILocalFile::initWithPath throws when a path is relative.
  656.  
  657.       file = this._dirSvc.get("XCurProcD", Ci.nsIFile);
  658.  
  659.       try {
  660.         file.append(aPath);
  661.         if (file.exists())
  662.           return file;
  663.       }
  664.       catch(ex) {}
  665.     }
  666.  
  667.     return null;
  668.   },
  669.  
  670.  
  671.   //**************************************************************************//
  672.   // Storage Methods
  673.  
  674.   _storePreferredAction: function HS__storePreferredAction(aHandlerInfo) {
  675.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  676.  
  677.     switch(aHandlerInfo.preferredAction) {
  678.       case Ci.nsIHandlerInfo.saveToDisk:
  679.         this._setLiteral(infoID, NC_SAVE_TO_DISK, "true");
  680.         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
  681.         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
  682.         break;
  683.  
  684.       case Ci.nsIHandlerInfo.handleInternally:
  685.         this._setLiteral(infoID, NC_HANDLE_INTERNALLY, "true");
  686.         this._removeTarget(infoID, NC_SAVE_TO_DISK);
  687.         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
  688.         break;
  689.  
  690.       case Ci.nsIHandlerInfo.useSystemDefault:
  691.         this._setLiteral(infoID, NC_USE_SYSTEM_DEFAULT, "true");
  692.         this._removeTarget(infoID, NC_SAVE_TO_DISK);
  693.         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
  694.         break;
  695.  
  696.       // This value is indicated in the datastore either by the absence of
  697.       // the three properties or by setting them all "false".  Of these two
  698.       // options, the former seems preferable, because it reduces the size
  699.       // of the RDF file and thus the amount of stuff we have to parse.
  700.       case Ci.nsIHandlerInfo.useHelperApp:
  701.       default:
  702.         this._removeTarget(infoID, NC_SAVE_TO_DISK);
  703.         this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
  704.         this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
  705.         break;
  706.     }
  707.   },
  708.  
  709.   _storePreferredHandler: function HS__storePreferredHandler(aHandlerInfo) {
  710.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  711.     var handlerID =
  712.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  713.  
  714.     var handler = aHandlerInfo.preferredApplicationHandler;
  715.  
  716.     if (handler) {
  717.       this._storeHandlerApp(handlerID, handler);
  718.  
  719.       // Make this app be the preferred app for the handler info.
  720.       //
  721.       // Note: nsExternalHelperAppService::FillContentHandlerProperties ignores
  722.       // this setting and instead identifies the preferred app as the resource
  723.       // whose URI follows the pattern urn:<class>:externalApplication:<type>.
  724.       // But the old downloadactions.js code used to set this property, so just
  725.       // in case there is still some code somewhere that relies on its presence,
  726.       // we set it here.
  727.       this._setResource(infoID, NC_PREFERRED_APP, handlerID);
  728.     }
  729.     else {
  730.       // There isn't a preferred handler.  Remove the existing record for it,
  731.       // if any.
  732.       this._removeTarget(infoID, NC_PREFERRED_APP);
  733.       this._removeAssertions(handlerID);
  734.     }
  735.   },
  736.  
  737.   /**
  738.    * Store the list of possible handler apps for the content type represented
  739.    * by the given handler info object.
  740.    *
  741.    * @param aHandlerInfo  {nsIHandlerInfo}  the handler info object
  742.    */
  743.   _storePossibleHandlers: function HS__storePossibleHandlers(aHandlerInfo) {
  744.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  745.  
  746.     // First, retrieve the set of handler apps currently stored for the type,
  747.     // keeping track of their IDs in a hash that we'll use to determine which
  748.     // ones are no longer valid and should be removed.
  749.     var currentHandlerApps = {};
  750.     var currentHandlerTargets = this._getTargets(infoID, NC_POSSIBLE_APP);
  751.     while (currentHandlerTargets.hasMoreElements()) {
  752.       let handlerApp = currentHandlerTargets.getNext();
  753.       if (handlerApp instanceof Ci.nsIRDFResource) {
  754.         let handlerAppID = handlerApp.ValueUTF8;
  755.         currentHandlerApps[handlerAppID] = true;
  756.       }
  757.     }
  758.  
  759.     // Next, store any new handler apps.
  760.     var newHandlerApps =
  761.       aHandlerInfo.possibleApplicationHandlers.enumerate();
  762.     while (newHandlerApps.hasMoreElements()) {
  763.       let handlerApp =
  764.         newHandlerApps.getNext().QueryInterface(Ci.nsIHandlerApp);
  765.       let handlerAppID = this._getPossibleHandlerAppID(handlerApp);
  766.       if (!this._hasResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID)) {
  767.         this._storeHandlerApp(handlerAppID, handlerApp);
  768.         this._addResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID);
  769.       }
  770.       delete currentHandlerApps[handlerAppID];
  771.     }
  772.  
  773.     // Finally, remove any old handler apps that aren't being used anymore,
  774.     // and if those handler apps aren't being used by any other type either,
  775.     // then completely remove their record from the datastore so we don't
  776.     // leave it clogged up with information about handler apps we don't care
  777.     // about anymore.
  778.     for (let handlerAppID in currentHandlerApps) {
  779.       this._removeResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID);
  780.       if (!this._existsResourceTarget(NC_POSSIBLE_APP, handlerAppID))
  781.         this._removeAssertions(handlerAppID);
  782.     }
  783.   },
  784.  
  785.   /**
  786.    * Store the given handler app.
  787.    *
  788.    * Note: the reason this method takes the ID of the handler app in a param
  789.    * is that the ID is different than it usually is when the handler app
  790.    * in question is a preferred handler app, so this method can't just derive
  791.    * the ID of the handler app by calling _getPossibleHandlerAppID, its callers
  792.    * have to do that for it.
  793.    *
  794.    * @param aHandlerAppID {string}        the ID of the handler app to store
  795.    * @param aHandlerApp   {nsIHandlerApp} the handler app to store
  796.    */
  797.   _storeHandlerApp: function HS__storeHandlerApp(aHandlerAppID, aHandlerApp) {
  798.     aHandlerApp.QueryInterface(Ci.nsIHandlerApp);
  799.     this._setLiteral(aHandlerAppID, NC_PRETTY_NAME, aHandlerApp.name);
  800.  
  801.     // In the case of the preferred handler, the handler ID could have been
  802.     // used to refer to a different kind of handler in the past (i.e. either
  803.     // a local hander or a web handler), so if the new handler is a local
  804.     // handler, then we remove any web handler properties and vice versa.
  805.     // This is unnecessary but harmless for possible handlers.
  806.  
  807.     if (aHandlerApp instanceof Ci.nsILocalHandlerApp) {
  808.       this._setLiteral(aHandlerAppID, NC_PATH, aHandlerApp.executable.path);
  809.       this._removeTarget(aHandlerAppID, NC_URI_TEMPLATE);
  810.       this._removeTarget(aHandlerAppID, NC_METHOD);
  811.       this._removeTarget(aHandlerAppID, NC_SERVICE);
  812.       this._removeTarget(aHandlerAppID, NC_OBJPATH);
  813.       this._removeTarget(aHandlerAppID, NC_INTERFACE);
  814.     }
  815.     else if(aHandlerApp instanceof Ci.nsIWebHandlerApp){
  816.       aHandlerApp.QueryInterface(Ci.nsIWebHandlerApp);
  817.       this._setLiteral(aHandlerAppID, NC_URI_TEMPLATE, aHandlerApp.uriTemplate);
  818.       this._removeTarget(aHandlerAppID, NC_PATH);
  819.       this._removeTarget(aHandlerAppID, NC_METHOD);
  820.       this._removeTarget(aHandlerAppID, NC_SERVICE);
  821.       this._removeTarget(aHandlerAppID, NC_OBJPATH);
  822.       this._removeTarget(aHandlerAppID, NC_INTERFACE);
  823.     }
  824.     else if(aHandlerApp instanceof Ci.nsIDBusHandlerApp){
  825.       aHandlerApp.QueryInterface(Ci.nsIDBusHandlerApp);
  826.       this._setLiteral(aHandlerAppID, NC_SERVICE, aHandlerApp.service);
  827.       this._setLiteral(aHandlerAppID, NC_METHOD, aHandlerApp.method);
  828.       this._setLiteral(aHandlerAppID, NC_OBJPATH, aHandlerApp.objectPath);
  829.       this._setLiteral(aHandlerAppID, NC_INTERFACE, aHandlerApp.dBusInterface);
  830.       this._removeTarget(aHandlerAppID, NC_PATH);
  831.       this._removeTarget(aHandlerAppID, NC_URI_TEMPLATE);
  832.     }
  833.     else {
  834.     throw "unknown handler type";
  835.     }
  836.     
  837.   },
  838.  
  839.   _storeAlwaysAsk: function HS__storeAlwaysAsk(aHandlerInfo) {
  840.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  841.     this._setLiteral(infoID,
  842.                      NC_ALWAYS_ASK,
  843.                      aHandlerInfo.alwaysAskBeforeHandling ? "true" : "false");
  844.   },
  845.  
  846.  
  847.   //**************************************************************************//
  848.   // Convenience Getters
  849.  
  850.   // Observer Service
  851.   __observerSvc: null,
  852.   get _observerSvc() {
  853.     if (!this.__observerSvc)
  854.       this.__observerSvc =
  855.         Cc["@mozilla.org/observer-service;1"].
  856.         getService(Ci.nsIObserverService);
  857.     return this.__observerSvc;
  858.   },
  859.  
  860.   // Directory Service
  861.   __dirSvc: null,
  862.   get _dirSvc() {
  863.     if (!this.__dirSvc)
  864.       this.__dirSvc =
  865.         Cc["@mozilla.org/file/directory_service;1"].
  866.         getService(Ci.nsIProperties);
  867.     return this.__dirSvc;
  868.   },
  869.  
  870.   // MIME Service
  871.   __mimeSvc: null,
  872.   get _mimeSvc() {
  873.     if (!this.__mimeSvc)
  874.       this.__mimeSvc =
  875.         Cc["@mozilla.org/mime;1"].
  876.         getService(Ci.nsIMIMEService);
  877.     return this.__mimeSvc;
  878.   },
  879.  
  880.   // Protocol Service
  881.   __protocolSvc: null,
  882.   get _protocolSvc() {
  883.     if (!this.__protocolSvc)
  884.       this.__protocolSvc =
  885.         Cc["@mozilla.org/uriloader/external-protocol-service;1"].
  886.         getService(Ci.nsIExternalProtocolService);
  887.     return this.__protocolSvc;
  888.   },
  889.  
  890.   // RDF Service
  891.   __rdf: null,
  892.   get _rdf() {
  893.     if (!this.__rdf)
  894.       this.__rdf = Cc["@mozilla.org/rdf/rdf-service;1"].
  895.                    getService(Ci.nsIRDFService);
  896.     return this.__rdf;
  897.   },
  898.  
  899.   // RDF Container Utils
  900.   __containerUtils: null,
  901.   get _containerUtils() {
  902.     if (!this.__containerUtils)
  903.       this.__containerUtils = Cc["@mozilla.org/rdf/container-utils;1"].
  904.                               getService(Ci.nsIRDFContainerUtils);
  905.     return this.__containerUtils;
  906.   },
  907.  
  908.   // RDF datasource containing content handling config (i.e. mimeTypes.rdf)
  909.   __ds: null,
  910.   get _ds() {
  911.     if (!this.__ds) {
  912.       var file = this._dirSvc.get("UMimTyp", Ci.nsIFile);
  913.       // FIXME: make this a memoizing getter if we use it anywhere else.
  914.       var ioService = Cc["@mozilla.org/network/io-service;1"].
  915.                       getService(Ci.nsIIOService);
  916.       var fileHandler = ioService.getProtocolHandler("file").
  917.                         QueryInterface(Ci.nsIFileProtocolHandler);
  918.       this.__ds =
  919.         this._rdf.GetDataSourceBlocking(fileHandler.getURLSpecFromFile(file));
  920.     }
  921.  
  922.     return this.__ds;
  923.   },
  924.  
  925.  
  926.   //**************************************************************************//
  927.   // Datastore Utils
  928.  
  929.   /**
  930.    * Get the string identifying whether this is a MIME or a protocol handler.
  931.    * This string is used in the URI IDs of various RDF properties.
  932.    * 
  933.    * @param aHandlerInfo {nsIHandlerInfo} the handler for which to get the class
  934.    * 
  935.    * @returns {string} the class
  936.    */
  937.   _getClass: function HS__getClass(aHandlerInfo) {
  938.     if (aHandlerInfo instanceof Ci.nsIMIMEInfo)
  939.       return CLASS_MIMEINFO;
  940.     else
  941.       return CLASS_PROTOCOLINFO;
  942.   },
  943.  
  944.   /**
  945.    * Return the unique identifier for a content type record, which stores
  946.    * the value field plus a reference to the content type's handler info record.
  947.    *
  948.    * |urn:<class>:<type>|
  949.    *
  950.    * XXX: should this be a property of nsIHandlerInfo?
  951.    *
  952.    * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
  953.    * @param aType  {string} the type (a MIME type or protocol scheme)
  954.    *
  955.    * @returns {string} the ID
  956.    */
  957.   _getTypeID: function HS__getTypeID(aClass, aType) {
  958.     return "urn:" + aClass + ":" + aType;
  959.   },
  960.  
  961.   /**
  962.    * Return the unique identifier for a handler info record, which stores
  963.    * the preferredAction and alwaysAsk fields plus a reference to the preferred
  964.    * handler app.  Roughly equivalent to the nsIHandlerInfo interface.
  965.    *
  966.    * |urn:<class>:handler:<type>|
  967.    *
  968.    * FIXME: the type info record should be merged into the type record,
  969.    * since there's a one to one relationship between them, and this record
  970.    * merely stores additional attributes of a content type.
  971.    *
  972.    * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
  973.    * @param aType  {string} the type (a MIME type or protocol scheme)
  974.    *
  975.    * @returns {string} the ID
  976.    */
  977.   _getInfoID: function HS__getInfoID(aClass, aType) {
  978.     return "urn:" + aClass + ":handler:" + aType;
  979.   },
  980.  
  981.   /**
  982.    * Return the unique identifier for a preferred handler record, which stores
  983.    * information about the preferred handler for a given content type, including
  984.    * its human-readable name and the path to its executable (for a local app)
  985.    * or its URI template (for a web app).
  986.    * 
  987.    * |urn:<class>:externalApplication:<type>|
  988.    *
  989.    * XXX: should this be a property of nsIHandlerApp?
  990.    *
  991.    * FIXME: this should be an arbitrary ID, and we should retrieve it from
  992.    * the datastore for a given content type via the NC:ExternalApplication
  993.    * property rather than looking for a specific ID, so a handler doesn't
  994.    * have to change IDs when it goes from being a possible handler to being
  995.    * the preferred one (once we support possible handlers).
  996.    * 
  997.    * @param aClass {string} the class (CLASS_MIMEINFO or CLASS_PROTOCOLINFO)
  998.    * @param aType  {string} the type (a MIME type or protocol scheme)
  999.    * 
  1000.    * @returns {string} the ID
  1001.    */
  1002.   _getPreferredHandlerID: function HS__getPreferredHandlerID(aClass, aType) {
  1003.     return "urn:" + aClass + ":externalApplication:" + aType;
  1004.   },
  1005.  
  1006.   /**
  1007.    * Return the unique identifier for a handler app record, which stores
  1008.    * information about a possible handler for one or more content types,
  1009.    * including its human-readable name and the path to its executable (for a
  1010.    * local app) or its URI template (for a web app).
  1011.    *
  1012.    * Note: handler app IDs for preferred handlers are different.  For those,
  1013.    * see the _getPreferredHandlerID method.
  1014.    *
  1015.    * @param aHandlerApp  {nsIHandlerApp}   the handler app object
  1016.    */
  1017.   _getPossibleHandlerAppID: function HS__getPossibleHandlerAppID(aHandlerApp) {
  1018.     var handlerAppID = "urn:handler:";
  1019.  
  1020.     if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
  1021.       handlerAppID += "local:" + aHandlerApp.executable.path;
  1022.     else if(aHandlerApp instanceof Ci.nsIWebHandlerApp){
  1023.       aHandlerApp.QueryInterface(Ci.nsIWebHandlerApp);
  1024.       handlerAppID += "web:" + aHandlerApp.uriTemplate;
  1025.     }
  1026.     else if(aHandlerApp instanceof Ci.nsIDBusHandlerApp){
  1027.       aHandlerApp.QueryInterface(Ci.nsIDBusHandlerApp);
  1028.       handlerAppID += "dbus:" + aHandlerApp.service + " " + aHandlerApp.method + " " + aHandlerApp.uriTemplate;
  1029.     }else{
  1030.     throw "unknown handler type";
  1031.     }
  1032.     
  1033.     return handlerAppID;
  1034.   },
  1035.  
  1036.   /**
  1037.    * Get the list of types for the given class, creating the list if it doesn't
  1038.    * already exist. The class can be either CLASS_MIMEINFO or CLASS_PROTOCOLINFO
  1039.    * (i.e. the result of a call to _getClass).
  1040.    * 
  1041.    * |urn:<class>s|
  1042.    * |urn:<class>s:root|
  1043.    * 
  1044.    * @param aClass {string} the class for which to retrieve a list of types
  1045.    *
  1046.    * @returns {nsIRDFContainer} the list of types
  1047.    */
  1048.   _ensureAndGetTypeList: function HS__ensureAndGetTypeList(aClass) {
  1049.     var source = this._rdf.GetResource("urn:" + aClass + "s");
  1050.     var property =
  1051.       this._rdf.GetResource(aClass == CLASS_MIMEINFO ? NC_MIME_TYPES
  1052.                                                      : NC_PROTOCOL_SCHEMES);
  1053.     var target = this._rdf.GetResource("urn:" + aClass + "s:root");
  1054.  
  1055.     // Make sure we have an arc from the source to the target.
  1056.     if (!this._ds.HasAssertion(source, property, target, true))
  1057.       this._ds.Assert(source, property, target, true);
  1058.  
  1059.     // Make sure the target is a container.
  1060.     if (!this._containerUtils.IsContainer(this._ds, target))
  1061.       this._containerUtils.MakeSeq(this._ds, target);
  1062.  
  1063.     // Get the type list as an RDF container.
  1064.     var typeList = Cc["@mozilla.org/rdf/container;1"].
  1065.                    createInstance(Ci.nsIRDFContainer);
  1066.     typeList.Init(this._ds, target);
  1067.  
  1068.     return typeList;
  1069.   },
  1070.  
  1071.   /**
  1072.    * Make sure there are records in the datasource for the given content type
  1073.    * by creating them if they don't already exist.  We have to do this before
  1074.    * storing any specific data, because we can't assume the presence
  1075.    * of the records (the nsIHandlerInfo object might have been created
  1076.    * from the OS), and the records have to all be there in order for the helper
  1077.    * app service to properly construct an nsIHandlerInfo object for the type.
  1078.    *
  1079.    * Based on old downloadactions.js::_ensureMIMERegistryEntry.
  1080.    *
  1081.    * @param aHandlerInfo {nsIHandlerInfo} the type to make sure has a record
  1082.    */
  1083.   _ensureRecordsForType: function HS__ensureRecordsForType(aHandlerInfo) {
  1084.     // Get the list of types.
  1085.     var typeList = this._ensureAndGetTypeList(this._getClass(aHandlerInfo));
  1086.  
  1087.     // If there's already a record in the datastore for this type, then we
  1088.     // don't need to do anything more.
  1089.     var typeID = this._getTypeID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  1090.     var type = this._rdf.GetResource(typeID);
  1091.     if (typeList.IndexOf(type) != -1)
  1092.       return;
  1093.  
  1094.     // Create a basic type record for this type.
  1095.     typeList.AppendElement(type);
  1096.     this._setLiteral(typeID, NC_VALUE, aHandlerInfo.type);
  1097.     
  1098.     // Create a basic info record for this type.
  1099.     var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  1100.     this._setLiteral(infoID, NC_ALWAYS_ASK, "false");
  1101.     this._setResource(typeID, NC_HANDLER_INFO, infoID);
  1102.     // XXX Shouldn't we set preferredAction to useSystemDefault?
  1103.     // That's what it is if there's no record in the datastore; why should it
  1104.     // change to useHelperApp just because we add a record to the datastore?
  1105.     
  1106.     // Create a basic preferred handler record for this type.
  1107.     // XXX Not sure this is necessary, since preferred handlers are optional,
  1108.     // and nsExternalHelperAppService::FillHandlerInfoForTypeFromDS doesn't seem
  1109.     // to require the record , but downloadactions.js::_ensureMIMERegistryEntry
  1110.     // used to create it, so we'll do the same.
  1111.     var preferredHandlerID =
  1112.       this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type);
  1113.     this._setLiteral(preferredHandlerID, NC_PATH, "");
  1114.     this._setResource(infoID, NC_PREFERRED_APP, preferredHandlerID);
  1115.   },
  1116.  
  1117.   /**
  1118.    * Append known handlers of the given class to the given array.  The class
  1119.    * can be either CLASS_MIMEINFO or CLASS_PROTOCOLINFO.
  1120.    *
  1121.    * @param aHandlers   {array} the array of handlers to append to
  1122.    * @param aClass      {string} the class for which to append handlers
  1123.    */
  1124.   _appendHandlers: function HS__appendHandlers(aHandlers, aClass) {
  1125.     var typeList = this._ensureAndGetTypeList(aClass);
  1126.     var enumerator = typeList.GetElements();
  1127.  
  1128.     while (enumerator.hasMoreElements()) {
  1129.       var element = enumerator.getNext();
  1130.       
  1131.       // This should never happen.  If it does, that means our datasource
  1132.       // is corrupted with type list entries that point to literal values
  1133.       // instead of resources.  If it does happen, let's just do our best
  1134.       // to recover by ignoring this entry and moving on to the next one.
  1135.       if (!(element instanceof Ci.nsIRDFResource))
  1136.         continue;
  1137.  
  1138.       // Get the value of the element's NC:value property, which contains
  1139.       // the MIME type or scheme for which we're retrieving a handler info.
  1140.       var type = this._getValue(element.ValueUTF8, NC_VALUE);
  1141.       if (!type)
  1142.         continue;
  1143.  
  1144.       var handler;
  1145.       if (typeList.Resource.ValueUTF8 == "urn:mimetypes:root")
  1146.         handler = this._mimeSvc.getFromTypeAndExtension(type, null);
  1147.       else
  1148.         handler = this._protocolSvc.getProtocolHandlerInfo(type);
  1149.  
  1150.       aHandlers.appendElement(handler, false);
  1151.     }
  1152.   },
  1153.  
  1154.   /**
  1155.    * Whether or not a property of an RDF source has a value.
  1156.    *
  1157.    * @param sourceURI   {string}  the URI of the source
  1158.    * @param propertyURI {string}  the URI of the property
  1159.    * @returns           {boolean} whether or not the property has a value
  1160.    */
  1161.   _hasValue: function HS__hasValue(sourceURI, propertyURI) {
  1162.     var source = this._rdf.GetResource(sourceURI);
  1163.     var property = this._rdf.GetResource(propertyURI);
  1164.     return this._ds.hasArcOut(source, property);
  1165.   },
  1166.  
  1167.   /**
  1168.    * Get the value of a property of an RDF source.
  1169.    *
  1170.    * @param sourceURI   {string} the URI of the source
  1171.    * @param propertyURI {string} the URI of the property
  1172.    * @returns           {string} the value of the property
  1173.    */
  1174.   _getValue: function HS__getValue(sourceURI, propertyURI) {
  1175.     var source = this._rdf.GetResource(sourceURI);
  1176.     var property = this._rdf.GetResource(propertyURI);
  1177.  
  1178.     var target = this._ds.GetTarget(source, property, true);
  1179.  
  1180.     if (!target)
  1181.       return null;
  1182.     
  1183.     if (target instanceof Ci.nsIRDFResource)
  1184.       return target.ValueUTF8;
  1185.  
  1186.     if (target instanceof Ci.nsIRDFLiteral)
  1187.       return target.Value;
  1188.  
  1189.     return null;
  1190.   },
  1191.  
  1192.   /**
  1193.    * Get all targets for the property of an RDF source.
  1194.    *
  1195.    * @param sourceURI   {string} the URI of the source
  1196.    * @param propertyURI {string} the URI of the property
  1197.    * 
  1198.    * @returns {nsISimpleEnumerator} an enumerator of targets
  1199.    */
  1200.   _getTargets: function HS__getTargets(sourceURI, propertyURI) {
  1201.     var source = this._rdf.GetResource(sourceURI);
  1202.     var property = this._rdf.GetResource(propertyURI);
  1203.  
  1204.     return this._ds.GetTargets(source, property, true);
  1205.   },
  1206.  
  1207.   /**
  1208.    * Set a property of an RDF source to a literal value.
  1209.    *
  1210.    * @param sourceURI   {string} the URI of the source
  1211.    * @param propertyURI {string} the URI of the property
  1212.    * @param value       {string} the literal value
  1213.    */
  1214.   _setLiteral: function HS__setLiteral(sourceURI, propertyURI, value) {
  1215.     var source = this._rdf.GetResource(sourceURI);
  1216.     var property = this._rdf.GetResource(propertyURI);
  1217.     var target = this._rdf.GetLiteral(value);
  1218.     
  1219.     this._setTarget(source, property, target);
  1220.   },
  1221.  
  1222.   /**
  1223.    * Set a property of an RDF source to a resource target.
  1224.    *
  1225.    * @param sourceURI   {string} the URI of the source
  1226.    * @param propertyURI {string} the URI of the property
  1227.    * @param targetURI   {string} the URI of the target
  1228.    */
  1229.   _setResource: function HS__setResource(sourceURI, propertyURI, targetURI) {
  1230.     var source = this._rdf.GetResource(sourceURI);
  1231.     var property = this._rdf.GetResource(propertyURI);
  1232.     var target = this._rdf.GetResource(targetURI);
  1233.     
  1234.     this._setTarget(source, property, target);
  1235.   },
  1236.  
  1237.   /**
  1238.    * Assert an arc into the RDF datasource if there is no arc with the given
  1239.    * source and property; otherwise, if there is already an existing arc,
  1240.    * change it to point to the given target. _setLiteral and _setResource
  1241.    * call this after converting their string arguments into resources
  1242.    * and literals, and most callers should call one of those two methods
  1243.    * instead of this one.
  1244.    *
  1245.    * @param source    {nsIRDFResource}  the source
  1246.    * @param property  {nsIRDFResource}  the property
  1247.    * @param target    {nsIRDFNode}      the target
  1248.    */
  1249.   _setTarget: function HS__setTarget(source, property, target) {
  1250.     if (this._ds.hasArcOut(source, property)) {
  1251.       var oldTarget = this._ds.GetTarget(source, property, true);
  1252.       this._ds.Change(source, property, oldTarget, target);
  1253.     }
  1254.     else
  1255.       this._ds.Assert(source, property, target, true);
  1256.   },
  1257.  
  1258.   /**
  1259.    * Assert that a property of an RDF source has a resource target.
  1260.    * 
  1261.    * The difference between this method and _setResource is that this one adds
  1262.    * an assertion even if one already exists, which allows its callers to make
  1263.    * sets of assertions (i.e. to set a property to multiple targets).
  1264.    *
  1265.    * @param sourceURI   {string} the URI of the source
  1266.    * @param propertyURI {string} the URI of the property
  1267.    * @param targetURI   {string} the URI of the target
  1268.    */
  1269.   _addResourceAssertion: function HS__addResourceAssertion(sourceURI,
  1270.                                                            propertyURI,
  1271.                                                            targetURI) {
  1272.     var source = this._rdf.GetResource(sourceURI);
  1273.     var property = this._rdf.GetResource(propertyURI);
  1274.     var target = this._rdf.GetResource(targetURI);
  1275.     
  1276.     this._ds.Assert(source, property, target, true);
  1277.   },
  1278.  
  1279.   /**
  1280.    * Remove an assertion with a resource target.
  1281.    *
  1282.    * @param sourceURI   {string} the URI of the source
  1283.    * @param propertyURI {string} the URI of the property
  1284.    * @param targetURI   {string} the URI of the target
  1285.    */
  1286.   _removeResourceAssertion: function HS__removeResourceAssertion(sourceURI,
  1287.                                                                  propertyURI,
  1288.                                                                  targetURI) {
  1289.     var source = this._rdf.GetResource(sourceURI);
  1290.     var property = this._rdf.GetResource(propertyURI);
  1291.     var target = this._rdf.GetResource(targetURI);
  1292.  
  1293.     this._ds.Unassert(source, property, target);
  1294.   },
  1295.  
  1296.   /**
  1297.    * Whether or not a property of an RDF source has a given resource target.
  1298.    * 
  1299.    * @param sourceURI   {string} the URI of the source
  1300.    * @param propertyURI {string} the URI of the property
  1301.    * @param targetURI   {string} the URI of the target
  1302.    *
  1303.    * @returns {boolean} whether or not there is such an assertion
  1304.    */
  1305.   _hasResourceAssertion: function HS__hasResourceAssertion(sourceURI,
  1306.                                                            propertyURI,
  1307.                                                            targetURI) {
  1308.     var source = this._rdf.GetResource(sourceURI);
  1309.     var property = this._rdf.GetResource(propertyURI);
  1310.     var target = this._rdf.GetResource(targetURI);
  1311.  
  1312.     return this._ds.HasAssertion(source, property, target, true);
  1313.   },
  1314.  
  1315.   /**
  1316.    * Whether or not a property of an RDF source has a given literal value.
  1317.    * 
  1318.    * @param sourceURI   {string} the URI of the source
  1319.    * @param propertyURI {string} the URI of the property
  1320.    * @param value       {string} the literal value
  1321.    *
  1322.    * @returns {boolean} whether or not there is such an assertion
  1323.    */
  1324.   _hasLiteralAssertion: function HS__hasLiteralAssertion(sourceURI,
  1325.                                                          propertyURI,
  1326.                                                          value) {
  1327.     var source = this._rdf.GetResource(sourceURI);
  1328.     var property = this._rdf.GetResource(propertyURI);
  1329.     var target = this._rdf.GetLiteral(value);
  1330.  
  1331.     return this._ds.HasAssertion(source, property, target, true);
  1332.   },
  1333.  
  1334.   /**
  1335.    * Whether or not there is an RDF source that has the given property set to
  1336.    * the given literal value.
  1337.    * 
  1338.    * @param propertyURI {string} the URI of the property
  1339.    * @param value       {string} the literal value
  1340.    *
  1341.    * @returns {boolean} whether or not there is a source
  1342.    */
  1343.   _existsLiteralTarget: function HS__existsLiteralTarget(propertyURI, value) {
  1344.     var property = this._rdf.GetResource(propertyURI);
  1345.     var target = this._rdf.GetLiteral(value);
  1346.  
  1347.     return this._ds.hasArcIn(target, property);
  1348.   },
  1349.  
  1350.   /**
  1351.    * Get the source for a property set to a given literal value.
  1352.    *
  1353.    * @param propertyURI {string} the URI of the property
  1354.    * @param value       {string} the literal value
  1355.    */
  1356.   _getSourceForLiteral: function HS__getSourceForLiteral(propertyURI, value) {
  1357.     var property = this._rdf.GetResource(propertyURI);
  1358.     var target = this._rdf.GetLiteral(value);
  1359.  
  1360.     var source = this._ds.GetSource(property, target, true);
  1361.     if (source)
  1362.       return source.ValueUTF8;
  1363.  
  1364.     return null;
  1365.   },
  1366.  
  1367.   /**
  1368.    * Whether or not there is an RDF source that has the given property set to
  1369.    * the given resource target.
  1370.    * 
  1371.    * @param propertyURI {string} the URI of the property
  1372.    * @param targetURI   {string} the URI of the target
  1373.    *
  1374.    * @returns {boolean} whether or not there is a source
  1375.    */
  1376.   _existsResourceTarget: function HS__existsResourceTarget(propertyURI,
  1377.                                                            targetURI) {
  1378.     var property = this._rdf.GetResource(propertyURI);
  1379.     var target = this._rdf.GetResource(targetURI);
  1380.  
  1381.     return this._ds.hasArcIn(target, property);
  1382.   },
  1383.  
  1384.   /**
  1385.    * Remove a property of an RDF source.
  1386.    *
  1387.    * @param sourceURI   {string} the URI of the source
  1388.    * @param propertyURI {string} the URI of the property
  1389.    */
  1390.   _removeTarget: function HS__removeTarget(sourceURI, propertyURI) {
  1391.     var source = this._rdf.GetResource(sourceURI);
  1392.     var property = this._rdf.GetResource(propertyURI);
  1393.  
  1394.     if (this._ds.hasArcOut(source, property)) {
  1395.       var target = this._ds.GetTarget(source, property, true);
  1396.       this._ds.Unassert(source, property, target);
  1397.     }
  1398.   },
  1399.  
  1400.  /**
  1401.   * Remove all assertions about a given RDF source.
  1402.   *
  1403.   * Note: not recursive.  If some assertions point to other resources,
  1404.   * and you want to remove assertions about those resources too, you need
  1405.   * to do so manually.
  1406.   *
  1407.   * @param sourceURI {string} the URI of the source
  1408.   */
  1409.   _removeAssertions: function HS__removeAssertions(sourceURI) {
  1410.     var source = this._rdf.GetResource(sourceURI);
  1411.     var properties = this._ds.ArcLabelsOut(source);
  1412.  
  1413.     while (properties.hasMoreElements()) {
  1414.       let property = properties.getNext();
  1415.       let targets = this._ds.GetTargets(source, property, true);
  1416.       while (targets.hasMoreElements()) {
  1417.         let target = targets.getNext();
  1418.         this._ds.Unassert(source, property, target);
  1419.       }
  1420.     }
  1421.   }
  1422.  
  1423. };
  1424.  
  1425.  
  1426. //****************************************************************************//
  1427. // More XPCOM Plumbing
  1428.  
  1429. function NSGetModule(compMgr, fileSpec) {
  1430.   return XPCOMUtils.generateModule([HandlerService]);
  1431. }
  1432.